#include "floppy.h"
#include <interrupt/irq.h>
#include <io.h>
#include <system/mod.h>


module_t floppy_module =
{
	.name = "floppy\0",
	.author = "dak\0",
	.license = "gpl\0",
	.desc = "\0",
	.dep = "timer\0",
	.type = MOD_TYPE_DEV_STORAGE,
	
	.init = floppy_init,
	.shut = floppy_shut,
};


static const char * drive_types[8] = {
    "none",
    "360kB 5.25\"",
    "1.2MB 5.25\"",
    "720kB 3.5\"",

    "1.44MB 3.5\"",
    "2.88MB 3.5\"",
    "unknown type",
    "unknown type"
};

/*
 * Handler del floppy
 */
static void floppy_callback(regs_t *r)
{
	floppy_int = 1;
}


/*
 * Testa le funzioni del floppy 
 */
void floppy_test()
{
	mod_initm(mod_get("floppy"), NULL);
	floppy_detect_drives();
	printf("%c %c\n",floppy_read_data(0),floppy_read_data(1));
}



/*
 * Scrive un commando
 */
void floppy_write_cmd(uint8_t cmd)
{
    int i;
    for(i = 0; i < 600; i++)
    {
        timer_sleep(1); // sleep 10 ms
        if(0x80 & io_inb(FLOPPY_REG_MAIN_STATUS))
			return (void) io_outb(FLOPPY_REG_DATA_FIFO, cmd);
    }  
}



/*
 * Leggi un dato dalla posizione corrente della testina di lettura
 */
uint8_t floppy_read_data()
{

    int i; // do timeout, 60 seconds
    for(i = 0; i < 600; i++)
    {
        timer_sleep(1); // sleep 10 ms
        if(0x80 & io_inb(FLOPPY_REG_MAIN_STATUS))
            return io_inb(FLOPPY_REG_DATA_FIFO);
    }
    return 0;
}


/*
 * Individua i drive
 */
void floppy_detect_drives()
{
	io_outb(0x70, 0x10);
   unsigned drives = io_inb(0x71);

   printf(" - Floppy drive 0: %s\n", drive_types[drives >> 4]);
   printf(" - Floppy drive 1: %s\n", drive_types[drives & 0xf]);
}


/*
 * Inizializza il floppy
 */
uint32_t floppy_init()
{
	irq_install_handler(IRQ_FLOPPY, &floppy_callback);
	floppy_reset();
}    


/*
 * Deinizializza il floppy
 */
uint32_t floppy_shut()
{
	irq_install_handler(IRQ_FLOPPY, NULL);
}   
     
     
int32_t floppy_seek(unsigned cyli, int head)
{
 /*   unsigned i, st0, cyl = -1; // set to bogus cylinder

    floppy_motor(base, floppy_motor_on);

    for(i = 0; i < 10; i++) {
        // Attempt to position to given cylinder
        // 1st byte bit[1:0] = drive, bit[2] = head
        // 2nd byte is cylinder number
        floppy_write_cmd(base, CMD_SEEK);
        floppy_write_cmd(base, head<<2);
        floppy_write_cmd(base, cyli);

        irq_wait(floppy_irq);
        floppy_check_interrupt(base, &st0, &cyl);

        if(st0 & 0xC0) {
            static const char * status[] =
            { "normal", "error", "invalid", "drive" };
            printk("floppy_seek: status = %s\n", status[st0 >> 6]);
            continue;
        }

        if(cyl == cyli) {
            floppy_motor(base, floppy_motor_off);
            return 0;
        }

    }

    printk("floppy_seek: 10 retries exhausted\n");
    floppy_motor(base, floppy_motor_off);*/
    return -1;	
}


/*
 * Resetta il floppy
 */
int32_t floppy_reset()
{
    io_outb(FLOPPY_REG_DIGITAL_OUTPUT, 0x00); // disable controller
    io_outb(FLOPPY_REG_DIGITAL_OUTPUT, 0x0C); // enable controller

   while(!floppy_int); // sleep until interrupt occurs
    //{
        floppy_int = 0;
        int st0, cyl; // ignore these here..
        floppy_check_interrupt(&st0, &cyl);
    //}

    // set transfer speed 500kb/s
    io_outb(FLOPPY_REG_CONFIGURATION_CONTROL, 0x00);

    //  - 1st byte is: bits[7:4] = steprate, bits[3:0] = head unload time
    //  - 2nd byte is: bits[7:1] = head load time, bit[0] = no-DMA
    //
    //  steprate    = (8.0ms - entry*0.5ms)*(1MB/s / xfer_rate)
    //  head_unload = 8ms * entry * (1MB/s / xfer_rate), where entry 0 -> 16
    //  head_load   = 1ms * entry * (1MB/s / xfer_rate), where entry 0 -> 128
    //
    floppy_write_cmd(FLOPPY_CMD_SPECIFY);
    floppy_write_cmd(0xdf); /* steprate = 3ms, unload time = 240ms */
    floppy_write_cmd(0x02); /* load time = 16ms, no-DMA = 0 */

    // it could fail...
    if(floppy_calibrate()) return -1;
}


/*
 * Calibra il floppy
 */
int32_t floppy_calibrate()
{

    int i, st0, cyl = -1; // set to bogus cylinder

    //floppy_motor(floppy_motor_on);

    for(i = 0; i < 10; i++) {
        // Attempt to positions head to cylinder 0
        floppy_write_cmd(FLOPPY_CMD_RECALIBRATE);
        floppy_write_cmd(0); // argument is drive, we only support 0

        while(!floppy_int);
        floppy_int = 0;
        floppy_check_interrupt(&st0, &cyl);
       
        if(st0 & 0xC0) {
            static const char * status[] =
            { 0, "error", "invalid", "drive" };
            printf("floppy_calibrate: status = %s\n", status[st0 >> 6]);
            continue;
        }

        if(!cyl) { // found cylinder 0 ?
            //floppy_motor(floppy_motor_off);
            return 0;
        }
    }

    printf("floppy_calibrate: 10 retries exhausted\n");
    //floppy_motor(floppy_motor_off);
    return -1;
}


void floppy_check_interrupt(int32_t *st0, int32_t *cyl)
{
    floppy_write_cmd(FLOPPY_CMD_SENSE_INTERRUPT);

    *st0 = floppy_read_data();
    *cyl = floppy_read_data();
}


void floppy_motor(int onoff) { }
/*
    if(onoff) {
        if(!floppy_motor_state) {
            // need to turn on
            out8_p(base + FLOPPY_DOR, 0x1c);
            timer_sleep(50); // wait 500 ms = hopefully enough for modern drives
        }
        floppy_motor_state = floppy_motor_on;
    } else {
        if(floppy_motor_state == floppy_motor_wait) {
            printk("floppy_motor: strange, fd motor-state already waiting..\n");
        }
        floppy_motor_ticks = 300; // 3 seconds, see floppy_timer() below
        floppy_motor_state = floppy_motor_wait;
    }
}

void floppy_motor_kill(int base) {
    out8_p(base + FLOPPY_DOR, 0x0c);
    floppy_motor_state = floppy_motor_off;
}

//
// THIS SHOULD BE STARTED IN A SEPARATE THREAD.
//
//
void floppy_timer() {
    while(1) {
        // sleep for 500ms at a time, which gives around half
        // a second jitter, but that's hardly relevant here.
        timer_sleep(50);
        if(floppy_motor_state == floppy_motor_wait) {
            floppy_motor_ticks -= 50;
            if(floppy_motor_ticks <= 0) {
                floppy_motor_kill(floppy_base);
            }
        }
    }
}
*/
